O objetivo desta atividade é aplicar conceitos de manipulação e visualização de dados para agrupar dados e comparar os diferentes grupos através de visualizações. À medida que as análises são feitas, vamos introduzindo diferentes possibilidades de visualização, destacando as vantagens e desvantagens de cada uma.
Vamos botar a mão na massa! Siga os passos executando o código no RStudio. De preferência, crie um novo documento Rmarkdown para ir adicionando os blocos de código e suas análises.
Primeiro, instale os pacotes que você ainda não tiver instalado antes (pode remover da lista os que você já tem):
pkgs <- c("tidyverse", "geofacet", "here", "patchwork", "scales", "zoo")
install.packages(pkgs, dependencies = TRUE)
library(tidyverse) # conjunto de pacotes que sempre iremos usar
library(geofacet) # organiza plots de acordo com região
library(here) # acesso a arquivos relativos à partir da raíz do projeto
library(lubridate) # manipulação de datas (tidyverse)
library(patchwork) # organiza o layout dos plots
library(readxl) # carrega arquivos excel (tidyverse)
library(scales) # funções úteis para lidar com escalas de gráficos
library(zoo) # função de média móvel (rolling average)
Vamos carregar os dados da COVID-19 disponibilizados no Painel Coronavírus do Ministério da Saúde. Para baixar os dados do site, você clica no botão Arquivo CSV (apesar do formato do arquivo ser .xlsx). No meu caso, o arquivo que baixei chamado HIST_PAINEL_COVIDBR_23jun2020.xlsx está no diretório data localizada na raíz do projeto. Se o seu arquivo estiver em outro local ou tiver outro nome, mude a localização no comando here() abaixo, que acessa arquivos a partir da raíz do projeto, seguindo os caminhos separados por vírgula. Rode e veja se os dados foram carregados:
covid <- read_xlsx(here("data", "HIST_PAINEL_COVIDBR_24jun2020.xlsx"),
guess_max = 100000)
glimpse(covid)
Rows: 456,538
Columns: 17
$ regiao <chr> "Brasil", "Brasil", "Brasil", "Brasil", "Brasil…
$ estado <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ municipio <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ coduf <chr> "76", "76", "76", "76", "76", "76", "76", "76",…
$ codmun <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ codRegiaoSaude <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ nomeRegiaoSaude <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ data <dttm> 2020-02-25, 2020-02-26, 2020-02-27, 2020-02-28…
$ semanaEpi <dbl> 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 11, …
$ populacaoTCU2019 <chr> "210147125", "210147125", "210147125", "2101471…
$ casosAcumulado <dbl> 0, 1, 1, 1, 2, 2, 2, 2, 3, 7, 13, 19, 25, 25, 3…
$ casosNovos <dbl> 0, 1, 0, 0, 1, 0, 0, 0, 1, 4, 6, 6, 6, 0, 9, 18…
$ obitosAcumulado <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ obitosNovos <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ Recuperadosnovos <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ emAcompanhamentoNovos <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ FgMetro <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
O arquivo fornecido pelo Ministério da Saúde possui dados agregados em 3 níveis de agregação: para todo o Brasil, por estado e por município. Vamos explorá-los.
Quando os dados são agregados para todo o Brasil, as colunas estado e codmun (código do município) não possuem valores – em R, elas possuem o código NA, que representa um valor não existente. O comando is.na() retorna TRUE se o valor para aquela coluna não existe ou FALSE caso contrário.
Desta forma, podemos filtrar os dados agregados para todo o Brasil retornando apenas as linhas onde as colunas estado e codmun são NA. Usando o comando filter(), quando as condições são passadas separadas por vírgula, o operador AND é aplicado; ou seja, só serão retornadas as linhas que satisfizerem todas essas condições, como no caso abaixo.
covid_pais <- covid %>%
filter(is.na(estado), is.na(codmun))
glimpse(covid_pais)
Rows: 121
Columns: 17
$ regiao <chr> "Brasil", "Brasil", "Brasil", "Brasil", "Brasil…
$ estado <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ municipio <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ coduf <chr> "76", "76", "76", "76", "76", "76", "76", "76",…
$ codmun <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ codRegiaoSaude <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ nomeRegiaoSaude <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ data <dttm> 2020-02-25, 2020-02-26, 2020-02-27, 2020-02-28…
$ semanaEpi <dbl> 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 11, …
$ populacaoTCU2019 <chr> "210147125", "210147125", "210147125", "2101471…
$ casosAcumulado <dbl> 0, 1, 1, 1, 2, 2, 2, 2, 3, 7, 13, 19, 25, 25, 3…
$ casosNovos <dbl> 0, 1, 0, 0, 1, 0, 0, 0, 1, 4, 6, 6, 6, 0, 9, 18…
$ obitosAcumulado <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ obitosNovos <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ Recuperadosnovos <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ emAcompanhamentoNovos <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ FgMetro <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
Outra forma de aplicar o operador AND para vetores em R é usando o símbolo & desta forma: filter(df, cond_A & cond_B), onde serão retornadas apenas as linhas do dataframe df que satisfizerem cond_A e cond_B. Para aplicar o operador OR para vetores, você usa símbolo | assim: filter(df, condA | condB), que retorná as linhas de df que satisfaçam condA ou condB.
Vamos agora visualizar (de forma bem simples) os dados agregados para o Brasil, que extraímos do filtro anterior:
p_casos_novos <- ggplot(covid_pais, aes(data, casosNovos)) +
geom_line()
p_casos_acc <- ggplot(covid_pais, aes(data, casosAcumulado)) +
geom_line()
p_obitos_novos <- ggplot(covid_pais, aes(data, obitosNovos)) +
geom_line()
p_obitos_acc <- ggplot(covid_pais, aes(data, obitosAcumulado)) +
geom_line()
# mostrando vários plots na mesma imagem com o pacote patchwork
p_casos_novos + p_casos_acc + p_obitos_novos + p_obitos_acc
Quando os dados são agregados por estado, não há valores na coluna codmun, mas há na coluna estado. A exclamação ! é o operador NOT do R, então usamos !is.na(estado) para filtrar as linhas onde o valor na coluna estado não é NA:
covid_estados <- covid %>%
filter(!is.na(estado), is.na(codmun))
glimpse(covid_estados)
Rows: 3,267
Columns: 17
$ regiao <chr> "Norte", "Norte", "Norte", "Norte", "Norte", "N…
$ estado <chr> "RO", "RO", "RO", "RO", "RO", "RO", "RO", "RO",…
$ municipio <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ coduf <chr> "11", "11", "11", "11", "11", "11", "11", "11",…
$ codmun <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ codRegiaoSaude <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ nomeRegiaoSaude <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ data <dttm> 2020-02-25, 2020-02-26, 2020-02-27, 2020-02-28…
$ semanaEpi <dbl> 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 11, …
$ populacaoTCU2019 <chr> "1777225", "1777225", "1777225", "1777225", "17…
$ casosAcumulado <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ casosNovos <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ obitosAcumulado <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ obitosNovos <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ Recuperadosnovos <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ emAcompanhamentoNovos <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ FgMetro <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
Quando os dados são agregados por município, há valores válidos nas colunas estado e municipio. Podemos filtrar assim:
covid_municipios <- covid %>%
filter(!is.na(estado), !is.na(municipio))
glimpse(covid_municipios)
Rows: 451,260
Columns: 17
$ regiao <chr> "Norte", "Norte", "Norte", "Norte", "Norte", "N…
$ estado <chr> "RO", "RO", "RO", "RO", "RO", "RO", "RO", "RO",…
$ municipio <chr> "Alta Floresta D'Oeste", "Alta Floresta D'Oeste…
$ coduf <chr> "11", "11", "11", "11", "11", "11", "11", "11",…
$ codmun <dbl> 110001, 110001, 110001, 110001, 110001, 110001,…
$ codRegiaoSaude <dbl> 11005, 11005, 11005, 11005, 11005, 11005, 11005…
$ nomeRegiaoSaude <chr> "ZONA DA MATA", "ZONA DA MATA", "ZONA DA MATA",…
$ data <dttm> 2020-03-27, 2020-03-28, 2020-03-29, 2020-03-30…
$ semanaEpi <dbl> 13, 13, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15,…
$ populacaoTCU2019 <chr> "22945", "22945", "22945", "22945", "22945", "2…
$ casosAcumulado <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ casosNovos <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ obitosAcumulado <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ obitosNovos <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ Recuperadosnovos <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ emAcompanhamentoNovos <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ FgMetro <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
Nós temos um “problema” para visualizar os casos por estado ou município, porque não conseguimos mostrar tudo com uma única linha. Vamos, então, visualizar os dados por estado, indicando que cada linha no gráfico vai representar um estado através do parâmetro group = estado:
ggplot(covid_estados, aes(data, casosAcumulado, group = estado)) +
geom_line()
Ops… está difícil distinguir qual o estado cada linha representa. Vamos ver abaixo algumas opções para melhorar a visualização quando queremos visualizar vários grupos ao mesmo tempo.
Uma forma de distinguir diferentes linhas é definindo que a propriedade de cor da linha (colour) será definida de acordo com a variável estado. Desta forma, cada estado terá sua própria cor de linha e também será exibida uma legenda para cada cor:
ggplot(covid_estados, aes(data, casosAcumulado, colour = estado)) +
geom_line()
Mas… quando a quantidade de grupos é muito grande fica difícil distinguir as cores e identificar facilmente cada estado.
Outra forma de separar os dados por estado é usando facets, que quebra o gráfico em painéis menores, como no comando facet_wrap() abaixo:
ggplot(covid_estados, aes(data, casosNovos)) +
geom_line() +
facet_wrap(vars(estado), ncol = 4)
Um problema de juntar vários estados em um mesmo gráfico é que eles possuem escalas diferentes. Por exemplo, enquanto SP tem mais de 200 mil casos, MS tem apenas 5 mil. Isso faz com que o gráfico de SP aumente a escala de todos os gráficos e a tendência de crescimento das curvas de estados com poucos casos ficam imperceptíveis.
Uma forma de contornar este problema é tornar as escalas de cada painel independente para o eixo y com o parâmetro scales = "free_y":
ggplot(covid_estados, aes(data, casosNovos)) +
geom_line() +
facet_wrap(vars(estado), ncol = 4, scales = "free_y")
Agora, conseguimos observar melhor a tendência dos novos casos ao longo do tempo para todos os estados. Mas… muito cuidado ao comparar gráficos em escalas diferentes!. Podemos ter a falsa impressão de que todos os estados estão numa situação muito parecida, mesmo os números sendo bem diferentes. Observe que SP chegou a ter mais de 15 mil novos casos em um dia, enquanto MS não passou de 400 novos casos em um dia. Então, ele é útil para compararmos tendências, mas não para compararmos valores.
Uma outra forma de conseguirmos ver melhor dados de grupos com faixas de valores muito diferentes em uma mesma escala é usando uma função para transformar as escalas. Na escala tradicional, a distância em um eixo entre os pontos 10 e 20 é a mesma da distância entre os pontos 20 e 30 – ou seja, tem uma relação linear com a diferença entre os pontos. Já na escala logarítmica, essas distâncias seriam diferentes. Por outro lado, numa escala log na base 10, a distância entre os valores 10 e 100, e 60 e 600 seriam iguais.
A escala log mostra valores relativos ao invés de valores absolutos. Por exemplo, se um município possui inicialmente 2 casos, na semana seguinte 4, depois 8. Seguindo essa tendência, em 5 semanas terá 32 casos e em 15 semanas mais de 32 mil casos. Em uma escala linear, a curva cresceria de forma muito rápida. Já na escala log, cada um desses aumentos teria a mesma distância, porque a cada semana a quantidade de casos aumenta em uma mesma taxa (100%), como podemos ver abaixo
dados <- tibble(semana = 1:15, casos = 2^semana)
p <- ggplot(dados, aes(semana, casos)) +
geom_line()
p1 <- p +
ggtitle("Escala linear")
p2 <- p +
scale_y_continuous(trans = "log2", breaks = 2^seq(1, 15, 2)) +
ggtitle("Escala log")
p1 + p2
Vamos visualizar o gráfico anterior usando uma escala logarítmica na base 10 para o eixo y:
ggplot(covid_estados, aes(data, casosNovos)) +
geom_line() +
facet_wrap(vars(estado), ncol = 4) +
scale_y_log10()
Note que agora o eixo y apresenta valores 1, 10, 100, 1000 com distâncias iguais entre eles. Então, este gráfico tende a apresentar um crescimento mais acentuado para valores mais baixos (ex: de 0 para 1000) e um crescimento mais suave para valores mais altos (ex: de 1000 para 2000).
A escala logarítmica faz sentido no contexto epidemiológico, já que a transmissão do vírus é feita de forma multiplicativa de acordo com a sua taxa de reprodução efetiva, que indica quantas pessoas um paciente com a doença infecta em média.
Um outro problema, que já vimos em uma aula anterior, é que os casos novos tem uma grande variação de um dia para o outro. Por exemplo, em fins de semana há uma tendência de menor notificação e em dias de semana de maior notificação.
Para amenizar este problema, podemos adicionar uma curva suavizada (ou smoothing) usando a função geom_smooth, que usa por padrão o método LOESS de suavização. Para os valores reais, vamos plotar pontos geom_point com transparência alpha = 0.2:
ggplot(covid_estados, aes(data, casosNovos)) +
geom_point(alpha = 0.2) +
geom_smooth() +
facet_wrap(vars(estado), ncol = 4) +
scale_y_log10()
A média móvel (rolling average) é outra métrica muito usada para suavizar a curva. Podemos adicionar nos dados uma nova coluna com o valor da média móvel, onde o ponto em um dia X representa a média de valores dos últimos 7 dias a partir do dia X. Para isto, vamos usar a função rollmean do pacote zoo (rode ?rollmean para ver a documentação). Vamos usar também o group_by para aplicar a média para os dados de cada estado isoladamente:
covid_estados <- covid_estados %>%
group_by(estado) %>%
mutate(casosNovosMediaMovel = rollmeanr(casosNovos, 7, fill = NA))
ggplot(covid_estados, aes(data, casosNovos)) +
geom_point(alpha = 0.2) +
geom_line(aes(y = casosNovosMediaMovel), col = "blue") +
facet_wrap(vars(estado), ncol = 4) +
scale_y_log10()
Por fim, vamos dar um “tapa no visu” no gráfico:
filtercol (é o mesmo que colour)sizefacet_geo para mostrar os subplots de acordo com a localização geográfica de cada estadoscale_color_brewerlabs() que modifica labelstheme_minimal), customizando posição da legenda e fontesggplot(filter(covid_estados, data >= lubridate::dmy("15-03-2020")),
aes(data, casosNovos, col = regiao)) +
geom_point(alpha = 0.1) +
geom_line(aes(y = casosNovosMediaMovel), size = 0.8) +
facet_geo(~ estado, grid = "br_states_grid1") +
scale_y_log10(labels = trans_format("log10", math_format(10^.x))) +
scale_color_brewer("Região", palette = "Set2") +
labs(title = "Casos novos por dia nos estados",
subtitle = "Em escala log. A linha representa a média móvel para 7 dias",
caption = "Fonte dos dados: Ministério da Saúde") +
theme_minimal() +
theme(legend.position = "top", axis.title = element_blank(),
strip.text = element_text(face = "bold"),
plot.title = element_text(face = "bold"))